//The MIT License
//
//Copyright (c) 2009 nodchip
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in
//all copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//THE SOFTWARE.
package tv.dyndns.kishibe.client.creation;

import java.util.ArrayList;
import java.util.List;

import tv.dyndns.kishibe.client.Controller;
import tv.dyndns.kishibe.client.Service;
import tv.dyndns.kishibe.client.UserData;
import tv.dyndns.kishibe.client.WidgetProblemReport;
import tv.dyndns.kishibe.client.game.FactoryPanelQuestion;
import tv.dyndns.kishibe.client.game.PanelQuestionBase;
import tv.dyndns.kishibe.client.game.WidgetTimer;
import tv.dyndns.kishibe.client.packet.PacketProblemData;
import tv.dyndns.kishibe.client.packet.PacketWrongAnswer;
import tv.dyndns.kishibe.client.ui.RequestedHtml;
import tv.dyndns.kishibe.client.util.SerialTimer;

import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;

public class PanelProblemCreation extends VerticalPanel implements ClickHandler {
	private static final int MAX_SIMILER_PROBLEMS_PER_PAGE = 10;
	private static final String TEMPLATE_IMAGE = "!<div>(ここに問題文を記述してください)</div><img src='__url__' height='240' />";
	private static final String TEMPLATE_MOVIE = "!<div>(ここに問題文を記述してください)</div><object width='435' height='320'><param name='movie' value='http://www.youtube.com/v/__id__&autoplay=1'></param><param name='wmode' value='transparent'></param><embed src='http://www.youtube.com/v/__id__&autoplay=1' type='application/x-shockwave-flash' wmode='transparent' width='435' height='320'></embed></object>";
	private final Checker[] CHECKERS = { new CheckerNull(this), new CheckerMarubatsu(this), new Checker4Taku(this), new Checker4Taku(this), new CheckerNarabekae(this), new CheckerMojiPanel(this), new CheckerTyping(this), new CheckerFlash(this), new CheckerEffect(this), new CheckerTato(this), new CheckerJunban(this), new CheckerSenmusubi(this), new CheckerSlot(this), new CheckerClick(this), new CheckerTegaki(this) };
	private WidgetProblemForm widgetProblemForm;
	private final TextBox textBoxGetProblem = new TextBox();
	private final TextBox textBoxTemplateImage = new TextBox();
	private final TextBox textBoxTemplateMovie = new TextBox();
	private final VerticalPanel panelButtons = new VerticalPanel();
	private final Button buttonMoveToVerification = new Button("送信確認画面に移動する", this);
	private final Button buttonSendProblem = new Button("問題を送信する", this);
	private final Button buttonNewProblem = new Button("入力内容をリセットする", this);
	private final Button buttonGetProblem = new Button("問題を修正する", this);
	private final Button buttonCopyProblem = new Button("問題をコピーする", this);
	private final Button buttonTemplateImage = new Button("画像問題文雛形作成", this);
	private final Button buttonTemplateMovie = new Button("動画問題文雛形作成", this);
	private final Button buttonCreateNextProblem = new Button("続けて問題を作成する", this);
	private final VerticalPanel panelWarning = new VerticalPanel();
	private final SimplePanel panelSimilar = new SimplePanel();
	private final SimplePanel panelWrongAnswer = new SimplePanel();
	private final SimplePanel panelSample = new SimplePanel();
	private final SimplePanel panelCreationLog = new SimplePanel();
	private final HTML htmlTypeCaution = new HTML();
	private final Timer timerCheckProblem = new SerialTimer() {
		public void run() {
			final boolean enabled = checkProblem();
			buttonMoveToVerification.setEnabled(enabled);
			buttonSendProblem.setEnabled(enabled);
		}
	};
	private boolean copyProblem;

	public PanelProblemCreation() {
		setWidth("800px");
		if (UserData.getInstance().getPlayCount() < 10) {
			add(new HTML("問題作成は10回以上プレイされた方のみに限定させて頂いております。申し訳ありません。<br/>10回以上プレイされてこの表示が表示されている場合はF5を押して再読み込みを行ってください。<br/>よろしくお願い致します。"));
			return;
		}

		buttonSendProblem.setTitle("似たような問題が無いことを確認しましたか？");

		makeWidget();
	}

	public void makeWidget() {
		clear();

		add(new HTML("問題ジャンルの偏りを減らすため、問題数の少ない問題への問題投稿に御協力ください。"));

		panelButtons.clear();
		panelButtons.add(buttonNewProblem);
		buttonMoveToVerification.setText("送信確認画面に移動する");
		panelButtons.add(buttonMoveToVerification);
		add(panelButtons);

		panelSimilar.clear();
		add(panelSimilar);

		panelWrongAnswer.clear();
		add(panelWrongAnswer);

		panelSample.clear();
		add(panelSample);

		add(new HTML("問題を修正する場合は以下に問題番号を入力してボタンし、問題を取得してからにしてください"));

		final HorizontalPanel panelGetProblem = new HorizontalPanel();
		textBoxGetProblem.setWidth("200px");
		textBoxGetProblem.setText("");
		panelGetProblem.add(textBoxGetProblem);
		panelGetProblem.add(buttonGetProblem);
		panelGetProblem.add(buttonCopyProblem);
		add(panelGetProblem);

		// 問題作成フォーム
		widgetProblemForm = new WidgetProblemForm(this);
		add(widgetProblemForm);

		add(panelWarning);

		add(htmlTypeCaution);

		panelCreationLog.clear();
		add(panelCreationLog);
		setCellHorizontalAlignment(panelCreationLog, ALIGN_CENTER);

		// 注意事項
		{
			final HTML html = new RequestedHtml("creation.html");
			html.setWidth("800px");
			add(html);
		}

		// 画像問題テンプレート
		final HorizontalPanel panelTemplateImage = new HorizontalPanel();
		panelTemplateImage.setVerticalAlignment(ALIGN_MIDDLE);
		panelTemplateImage.add(buttonTemplateImage);
		panelTemplateImage.add(new Label("画像URL"));
		panelTemplateImage.add(textBoxTemplateImage);
		textBoxTemplateImage.addClickHandler(this);
		textBoxTemplateImage.setText("(画像URLを貼り付けて下さい)");
		textBoxTemplateImage.setWidth("400px");
		add(panelTemplateImage);

		// 動画問題テンプレート
		final HorizontalPanel panelTemplateMovie = new HorizontalPanel();
		panelTemplateMovie.setVerticalAlignment(ALIGN_MIDDLE);
		panelTemplateMovie.add(buttonTemplateMovie);
		panelTemplateMovie.add(new Label("動画URL"));
		panelTemplateMovie.add(textBoxTemplateMovie);
		textBoxTemplateMovie.addClickHandler(this);
		textBoxTemplateMovie.setText("(YouTube動画URLを貼り付けて下さい)");
		textBoxTemplateMovie.setWidth("400px");
		add(panelTemplateMovie);
	}

	public void setTypeCaution(String html) {
		htmlTypeCaution.setHTML(html);
	}

	private boolean checkProblem() {
		panelWarning.clear();

		if (widgetProblemForm == null || widgetProblemForm.getListBoxType() == null) {
			return false;
		}
		final int typeIndex = widgetProblemForm.getListBoxType().getSelectedIndex();
		final int type = Integer.parseInt(widgetProblemForm.getListBoxType().getValue(typeIndex));

		return CHECKERS[type].check(widgetProblemForm);
	}

	private void setProblemSample(PacketProblemData problemData) {
		final WidgetTimer widgetTimer = new WidgetTimer();
		final PanelQuestionBase panelQuestion = FactoryPanelQuestion.getINSTANCE().create(problemData, widgetTimer);
		panelQuestion.setInputEnable(false);
		panelQuestion.displayRatio();
		panelSample.setWidget(panelQuestion);
	}

	private void moveToVerification() {
		if (!checkProblem()) {
			return;
		}

		buttonMoveToVerification.setText("問題を修正して再度送信確認画面に移動する");
		panelButtons.remove(buttonSendProblem);
		panelButtons.add(buttonSendProblem);

		final PacketProblemData problemData = widgetProblemForm.getProblemData();
		setProblemSample(problemData);
		panelSimilar.clear();
		getSimilarProblems(problemData);
	}

	private void getSimilarProblems(PacketProblemData problemData) {
		final String query = problemData.getSearchQuery();
		Service.Util.getInstance().searchSimilarProblem(query, callbackSearchSimilarProblem);
	}

	private final AsyncCallback<PacketProblemData[]> callbackSearchSimilarProblem = new AsyncCallback<PacketProblemData[]>() {
		public void onSuccess(PacketProblemData[] result) {
			panelSimilar.setWidget(new WidgetProblemReport(result, true, true, MAX_SIMILER_PROBLEMS_PER_PAGE));
		}

		public void onFailure(Throwable caught) {
			caught.printStackTrace();
		}
	};

	public void getWrongAnswers(int problemID) {
		Service.Util.getInstance().getWrongAnswers(problemID, callbackGetWrongAnswers);
	}

	private final AsyncCallback<PacketWrongAnswer[]> callbackGetWrongAnswers = new AsyncCallback<PacketWrongAnswer[]>() {
		public void onSuccess(PacketWrongAnswer[] result) {
			final StringBuffer buffer = new StringBuffer("誤答例: ");
			for (int i = 0; i < result.length; ++i) {
				buffer.append(result[i].answer);
				buffer.append("(");
				buffer.append(result[i].count);
				buffer.append(") ");
			}
			panelWrongAnswer.setWidget(new HTML(buffer.toString()));
		}

		public void onFailure(Throwable caught) {
			caught.printStackTrace();
		}
	};

	private void sendProblem() {
		if (!checkProblem()) {
			return;
		}

		timerCheckProblem.cancel();
		widgetProblemForm.setEnable(false);
		setEnable(false);

		final PacketProblemData problemData = widgetProblemForm.getProblemData();
		final int userCode = UserData.getInstance().getUserCode();
		Service.Util.getInstance().uploadProblem(problemData, userCode, callbackUploadProblem);
	}

	private final AsyncCallback<Integer> callbackUploadProblem = new AsyncCallback<Integer>() {
		public void onSuccess(Integer result) {
			final int userCode = UserData.getInstance().getUserCode();

			clear();
			add(new Label("問題の送信が完了しました"));
			add(new Label("問題番号は" + result + "番です"));
			add(buttonCreateNextProblem);

			Service.Util.getInstance().addProblemIdsToReport(userCode, new int[] { result }, Service.NULL_CALLBACK);

			timerCheckProblem.scheduleRepeating(1000);
			setEnable(true);
		}

		public void onFailure(Throwable caught) {
			Controller.getInstance().showErrorMessage("問題の送信に失敗しました");
			timerCheckProblem.scheduleRepeating(1000);
			setEnable(true);
			return;
		}
	};

	private void setEnable(boolean enabled) {
		buttonMoveToVerification.setEnabled(enabled);
		buttonSendProblem.setEnabled(enabled);
		buttonNewProblem.setEnabled(enabled);
		buttonGetProblem.setEnabled(enabled);
		buttonTemplateImage.setEnabled(enabled);
		buttonTemplateMovie.setEnabled(enabled);
	}

	private void getProblemFromServer(boolean copy) {
		copyProblem = copy;

		int problemId;
		try {
			problemId = Integer.parseInt(textBoxGetProblem.getText());
		} catch (NumberFormatException e) {
			Controller.getInstance().showErrorMessage("問題番号が間違っています");
			Controller.getInstance().showErrorMessage(e.getLocalizedMessage());
			return;
		}

		setEnable(false);

		final List<Integer> list = new ArrayList<Integer>();
		list.add(problemId);

		Service.Util.getInstance().getProblemList(list, callbackGetProblemList);

		if (copy) {
			panelCreationLog.clear();
		} else {
			panelCreationLog.setWidget(new PanelProblemCreationLog(problemId));
		}
	}

	private final AsyncCallback<PacketProblemData[]> callbackGetProblemList = new AsyncCallback<PacketProblemData[]>() {
		public void onSuccess(PacketProblemData[] result) {
			if (result == null || result.length == 0) {
				Controller.getInstance().showErrorMessage("無効な問題番号が指定されました");
				setEnable(true);
				return;
			}

			final PacketProblemData problemData = result[0];
			if (problemData == null) {
				Controller.getInstance().showErrorMessage("無効な問題番号が指定されました");
				setEnable(true);
				return;
			}

			widgetProblemForm.setProblemData(problemData, copyProblem);

			if (copyProblem) {
				panelSimilar.clear();
				panelWrongAnswer.clear();
				panelSample.clear();
				panelCreationLog.clear();
				setEnable(true);
			} else {
				setProblemSample(problemData);
				setEnable(true);

				getWrongAnswers(problemData.problemId);
				panelSimilar.clear();
				getSimilarProblems(problemData);
			}
		}

		public void onFailure(Throwable caught) {
			Controller.getInstance().showErrorMessage("問題の取得に失敗しました");
			setEnable(true);
		}
	};

	private void makeSentenceImage() {
		final String url = textBoxTemplateImage.getText();
		if (!url.startsWith("http://")) {
			return;
		}

		final String sentence = TEMPLATE_IMAGE.replaceAll("__url__", url);
		widgetProblemForm.getTextAreaSentence().setText(sentence);
	}

	private void makeSentenceMovie() {
		final String url = textBoxTemplateMovie.getText();
		if (!url.startsWith("http://www.youtube.com/watch?v=")) {
			return;
		}

		String id = url.substring(url.indexOf("=") + 1);
		int offset;
		if ((offset = id.indexOf("&")) != -1) {
			id = id.substring(0, offset);
		}

		final String sentence = TEMPLATE_MOVIE.replaceAll("__id__", id);
		widgetProblemForm.getTextAreaSentence().setText(sentence);
	}

	public VerticalPanel getPanelWarning() {
		return panelWarning;
	}

	public void setProblemData(int problemID) {
		textBoxGetProblem.setText(String.valueOf(problemID));
		getProblemFromServer(false);
	}

	protected void onLoad() {
		super.onLoad();
		timerCheckProblem.scheduleRepeating(1000);
	}

	protected void onUnload() {
		timerCheckProblem.cancel();
		super.onUnload();
	}

	@Override
	public void onClick(ClickEvent event) {
		final Object sender = event.getSource();
		if (sender == buttonMoveToVerification) {
			// 確認画面に移動する
			moveToVerification();

		} else if (sender == buttonSendProblem) {
			// 問題を送信する
			sendProblem();

		} else if (sender == buttonNewProblem) {
			// 新しい問題を作成する
			makeWidget();

		} else if (sender == buttonCreateNextProblem) {
			// 続けて問題を作成する
			makeWidget();

		} else if (sender == buttonGetProblem) {
			// 問題を取得する
			getProblemFromServer(false);

		} else if (sender == buttonCopyProblem) {
			// 問題をコピーする
			getProblemFromServer(true);

		} else if (sender == textBoxTemplateImage) {
			// 画像問題テンプレートテキストボックス反転
			textBoxTemplateImage.selectAll();

		} else if (sender == textBoxTemplateMovie) {
			// 動画問題テンプレートテキストボックス反転
			textBoxTemplateMovie.setSelectionRange(0, textBoxTemplateMovie.getText().length());

		} else if (sender == buttonTemplateImage) {
			// 画像問題テンプレート作成
			makeSentenceImage();

		} else if (sender == buttonTemplateMovie) {
			// 動画問題テンプレート作成
			makeSentenceMovie();

		}
	}
}
